<?php
/**
 * TaskRepo.php
 *
 * @copyright   2023 thingsworld.com.cn - All Rights Reserved
 * @link        http://thingsworld.com.cn/
 * @author      stiffer.chen <chenl@thingsworld.com.cn>
 * @created     2023-04-17 10:12:48
 * @modified    2023-04-17 10:12:48
 *
 */

namespace App\Repositories;

use App\Http\Resources\TaskResource;
use App\Library\Logger;
use App\Models\Product;
use App\Models\Project;
use App\Models\Project\Task;
use App\Models\UserModel;
use App\Services\DeptService;
use App\Services\SmsService;
use App\Services\TaskService;
use App\Services\UserService;
use Carbon\Carbon;

class TaskRepo
{
    const STATUS_UN_START = 'un_start';
    const STATUS_STARTED = 'started';
    const STATUS_COMPLETED = 'completed';
    const STATUS_CLOSED = 'closed';
    const STATUS_PAUSED = 'paused';

    const SCHEDULE_STATUS_UN_START = 'un_start';
    const SCHEDULE_STATUS_DELAY = 'delay'; // 拖延
    const SCHEDULE_STATUS_PROCESSING = 'processing'; // 进行中
    const SCHEDULE_STATUS_POSTPONE = 'postpone'; // 延期
    const SCHEDULE_STATUS_COMPLETE_BEFORE = 'complete_before'; // 提前完成
    const SCHEDULE_STATUS_COMPLETE_EQUAL = 'equal'; // 正常完成
    const SCHEDULE_STATUS_COMPLETE_AFTER = 'complete_after'; // 拖延完成

    const SMS_NOTIFY_CREATE = [
        'sms_code' => 'SMS_463685463',
        'variables' => [
            'task_number'
        ]
    ];

    private static $specialData = null;

    public static function schedule_status()
    {
        return [
            ['code' => self::SCHEDULE_STATUS_UN_START, 'title' => '未开始'],
            ['code' => self::SCHEDULE_STATUS_DELAY, 'title' => '进度拖延'],
            ['code' => self::SCHEDULE_STATUS_PROCESSING, 'title' => '进行中'],
            ['code' => self::SCHEDULE_STATUS_POSTPONE, 'title' => '进度延期'],
            ['code' => self::SCHEDULE_STATUS_COMPLETE_BEFORE, 'title' => '提前完成'],
            ['code' => self::SCHEDULE_STATUS_COMPLETE_EQUAL, 'title' => '正常完成'],
            ['code' => self::SCHEDULE_STATUS_COMPLETE_AFTER, 'title' => '拖延完成']
        ];
    }

    public static function status()
    {
        return [
            ['code' => self::STATUS_UN_START, 'title' => '未开始'],
            ['code' => self::STATUS_STARTED, 'title' => '进行中'],
            ['code' => self::STATUS_PAUSED, 'title' => '暂停'],
            ['code' => self::STATUS_COMPLETED, 'title' => '已完成'],
            ['code' => self::STATUS_CLOSED, 'title' => '已关闭'],
        ];
    }

    public static function getTypes()
    {
        $service = new TaskService();
        return $service->types();
    }

    /**
     * 所有任务清单
     * 查看的区域是指: 创建人或负责人所在区域
     * 普通用户: 参与的项目和任务
     * 超级管理员: 全部区域
     * 项目经理: 自己管理的项目的所有任务
     * 区域主管、区域经理: 管理区域内所有项目的任务
     */
    public static function listAll()
    {
        $limit = intval(request('limit', 10));
        $status = trim(request('status', ''));

        $builder = Task::query()
            ->select(['task.*'])
            ->join('task_user as tu', 'tu.task_id', '=', 'task.id')
            ->orderByDesc('update_time')
            ->orderByDesc('create_time');

        $userIds = self::listAllUserIds();
        if ($userIds) {
            $builder->where(function ($query) use ($userIds) {
                $query->whereIn('task.create_user_id', $userIds)
                    ->orWhere('tu.user_id', 'in', $userIds);
            });
        }

        if ($status) {
            $builder->where('status', $status);
        }

        $builder->with(['users.user', 'project', 'histories.user']);
        $result = $builder->paginate($limit);
        $result->transform(function ($item) {
            return new TaskResource($item);
        });
        return $result;
    }

    private static function listAllUserIds()
    {
        $currentUserId = registry('user_id');
        $userIds = [$currentUserId];
        $userService = new UserService();

        $isProjectManager = $userService->isProjectManager($currentUserId);
        $isZoneManager = $userService->isZoneManager($currentUserId);
        $isZoneCharge = $userService->isZoneCharge($currentUserId);
        if ($isProjectManager) {
            // @todo
        }
        if ($isZoneManager || $isZoneCharge) {
            $currentDeptId = registry('user')->dept_id;
            $allSubDeptIds = (new DeptService())->getAllChildIds($currentDeptId);
            $deptIds = array_unique(array_merge($allSubDeptIds, [$currentDeptId]));
            $deptUserIds = UserModel::query()
                ->whereIn('dept_id', $deptIds)
                ->select(['id'])
                ->get()
                ->pluck('id')
                ->toArray();
            $userIds = array_merge($userIds, $deptUserIds);
        }
        return $userIds;
    }

    public static function start($taskId)
    {
        $task = Task::find($taskId);
        if (empty($task)) {
            throw new \Exception("不存在的任务(#{$taskId})");
        }
        $taskStatus = $task->status;
        if ($taskStatus != self::STATUS_UN_START) {
            throw new \Exception("仅 未开始 的任务可以进行开始操作");
        }

        $startAt = trim(request()->get('start_at') ?? '');
        if (empty($startAt)) {
            throw new \Exception("请输入实际开始日期");
        }
        try {
            $startAt = Carbon::parse($startAt)->toDateString();
        } catch (\Exception $e) {
            throw new \Exception("开始日期格式错误，请检查");
        }
        $status = self::STATUS_STARTED;

        $task->start_at = $startAt;
        $task->status = $status;
        $task->saveOrFail();

        $history = new Task\History([
            'status' => $status,
            'user_id' => registry('user_id')
        ]);
        $task->histories()->save($history);

        return self::fetch($task->id);
    }

    public static function close($taskId)
    {
        $task = Task::find($taskId);
        if (empty($task)) {
            throw new \Exception("不存在的任务(#{$taskId})");
        }
        $taskStatus = $task->status;
        if ($taskStatus != self::STATUS_UN_START && $taskStatus != self::STATUS_STARTED) {
            throw new \Exception("仅 未开始、进行中 的任务可以进行关闭操作");
        }
        $reason = trim(request('close_reason') ?? '');
        if (!$reason) {
            throw new \Exception("请填写关闭原因");
        }

        $status = self::STATUS_CLOSED;
        $task->status = $status;
        $task->close_reason = $reason;
        $task->saveOrFail();

        $history = new Task\History([
            'status' => $status,
            'user_id' => registry('user_id')
        ]);
        $task->histories()->save($history);

        return self::fetch($taskId);
    }

    public static function complete($taskId)
    {
        $task = Task::find($taskId);
        if (empty($task)) {
            throw new \Exception("不存在的任务(#{$taskId})");
        }
        $taskStatus = $task->status;
        if ($taskStatus != self::STATUS_STARTED) {
            throw new \Exception("仅 已开始 的任务可以进行完成操作");
        }
        $completeAt = trim(request()->get('complete_at') ?? '');
        if (empty($completeAt)) {
            throw new \Exception("请输入实际完成日期");
        }
        try {
            $completeAt = Carbon::parse($completeAt)->toDateString();
        } catch (\Exception $e) {
            throw new \Exception("完成日期格式错误，请检查");
        }
        $taskStartAt = $task->start_at;
        if ($taskStartAt > $completeAt) {
            throw new \Exception("完成日期不能小于开始日期");
        }
        $workHour = floatval(request()->get('work_hour') ?? '');
        if ($workHour <= 0) {
            throw new \Exception("耗费工时错误");
        }
        $products = [];
        $taskProductIds = request()->get('product_ids') ?? [];
        foreach ($taskProductIds as $taskProductId) {
            $product = Product::find($taskProductId);
            if (empty($product)) {
                throw new \Exception("商品信息不存在(#{$taskProductId})");
            }
            $products[] = new Task\Product([
                'product_id' => $taskProductId
            ]);
        }

        $status = self::STATUS_COMPLETED;

        $task->status = $status;
        $task->complete_at = $completeAt;
        $task->work_hour += $workHour;
        $task->saveOrFail();

        $task->products()->saveMany($products);

        $history = new Task\History([
            'status' => $status,
            'user_id' => registry('user_id')
        ]);
        $task->histories()->save($history);

        $projectUserId = $task->project->user_id;

        /**
         * 项目完成后通知任务负责人
         * http://pm.thingsworld.com.cn/index.php?m=task&f=view&taskID=415
         */
        NotificationRepo::multiStore([$projectUserId], $taskId, NotificationRepo::TYPE_TASK, self::NOTIFY_COMPLETE['title'], sprintf(self::NOTIFY_COMPLETE['content'], $task->task_number));

        return self::fetch($task->id);
    }

    public static function fetch($taskId)
    {
        $task = Task::with(['users.user', 'products', 'project', 'histories.user'])->find($taskId);
        if (empty($task)) {
            throw new \Exception('任务不存在');
        }
        return self::detail($task);
    }

    public static function destroy($taskId)
    {
        $task = Task::find($taskId);
        if (empty($task)) {
            throw new \Exception('任务不存在');
        }
        if ($task->status != self::STATUS_CLOSED) {
            throw new \Exception('仅能删除已关闭的任务');
        }
        $task->mark = 0;
        $task->saveOrFail();
    }

    public static function getList()
    {
        $perPage = (int)request('limit');
        if ($perPage < 1) {
            $perPage = 1;
        }
        $projectId = intval(request()->get('project_id') ?? '');
        $name = trim(request()->get('name') ?? '');
        $taskNumber = trim(request()->get('task_number') ?? '');
        $status = trim(request()->get('status') ?? '');
        $returnClosed = intval(request()->get('return_closed') ?? '');

        $builder = Task::query();
        $builder->with(['users.user', 'products', 'project', 'histories.user']);
        $builder->where('mark', 1);
        if (!$returnClosed) {
            $builder->where('status', '<>', self::STATUS_CLOSED);
        }

        if ($projectId) {
            $builder->where('project_id', $projectId);
        }
        if ($name) {
            $builder->where('task.name', 'like', "%{$name}%");
        }
        if ($taskNumber) {
            $builder->where('task.task_number', '=', $taskNumber);
        }
        if ($status) {
            $builder->where('task.status', '=', $status);
        }
        $builder->orderByDesc('create_time');
        $data = $builder->paginate($perPage);
        $data->transform(function ($item) {
            return self::detail($item);
        });
        return $data;
    }

    public static function getActionContentByStatus($status)
    {
        switch ($status) {
            case self::STATUS_UN_START:
                return "创建了任务";
                break;
            case self::STATUS_STARTED:
                return "开始了任务";
                break;
            case self::STATUS_COMPLETED:
                return "完成了任务";
                break;
            case self::STATUS_CLOSED:
                return "关闭了任务";
                break;
            case self::STATUS_PAUSED:
                return "暂停了任务";
                break;
            default:
                return "";
        }
    }

    public static function detail($task)
    {
        return new TaskResource($task);
    }

    public static function products($taskId)
    {
        $task = Task::with(['products.product.customer'])->find($taskId);
        if (empty($task)) {
            return [];
        }
        $products = [];
        foreach ($task->products as $product) {
            $products[] = [
                'id' => $product->id,
                'name' => $product->product->name ?? '',
                'model' => $product->product->model ?? '',
                'customer_name' => $product->product->customer->name ?? ''
            ];
        }
        return $products;
    }

    public static function update($taskId)
    {
        $data = self::singleRequestData($taskId);

        $main = $data['main'];

        $status = $main['status'];

        $userIds = $data['user_ids'];

        $users = [];
        foreach ($userIds as $userId) {
            $users[] = new Task\User([
                'user_id' => $userId
            ]);
        }

        $task = Task::find($taskId);
        $task->fill($main);
        $task->saveOrFail();

        $history = new Task\History(['status' => $status]);
        $task->histories()->save($history);

        $task->users()->delete();
        $task->users()->saveMany($users);

        return self::fetch($taskId);
    }

    public static function store()
    {
        $data = self::requestData();

        $successCount = 0;

        $pushData = [];
        foreach ($data as $item) {
            $main = $item['main'];
            $taskNumber = self::generateNumber(0);
            $main['task_number'] = $taskNumber;
            $status = $main['status'];
            $userIds = $item['user_ids'];
            $users = [];
            foreach ($userIds as $userId) {
                $users[] = new Task\User([
                    'user_id' => $userId
                ]);
                // 发送通知
                self::sendSms($userId, self::SMS_NOTIFY_CREATE, ['task_number' => $taskNumber]);
            }
            $task = new Task();
            $task->fill($main);
            $task->saveOrFail();

            $task->users()->saveMany($users);

            $history = new Task\History([
                'status' => $status,
                'user_id' => registry('user_id')
            ]);
            $task->histories()->save($history);
            $successCount += 1;

            $taskId = $task->id;
            $pushData[] = [
                'task_id' => $taskId,
                'number' => $task->task_number,
                'user_ids' => $userIds
            ];

            NotificationRepo::multiStore($userIds, $taskId, NotificationRepo::TYPE_TASK, self::NOTIFY_CREATE['title'], sprintf(self::NOTIFY_CREATE['content'], $task->task_number));
        }
        return $successCount;
    }

    /**
     * 新建任务(分配)
     * 触发点: 新建任务(分配)
     * 接收对象: 负责人
     */
    const NOTIFY_CREATE = [
        'title' => '任务发布通知',
        'content' => "你有新任务需要处理"
    ];

    const NOTIFY_COMPLETE = [
        'title' => '任务完成通知',
        'content' => "任务: %s 已完成"
    ];

    public static function sendSms($userId, $template, $params = [])
    {
        $logger = new Logger('sms/task');
        $user = UserModel::find($userId);
        if (empty($user)) {
            $logger->error("用户数据不存在: {$userId}");
            return;
        }
        $telephone = $user->mobile ?? '';
        if (!$telephone) {
            $logger->error("用户手机号码为空: {$userId}");
            return;
        }
        $smsCode = $template['sms_code'];
        $smsService = new SmsService($telephone);
        try {
            $smsService->sendMessage($smsCode, $params);
        } catch (\Exception $e) {
            $logger->error($e->getMessage());
        }
    }

    public static function specialData()
    {
        if (!is_null(self::$specialData)) {
            return self::$specialData;
        }
        $builder = Task::query()
            ->from('task as t')
            ->select([
                't.id',
                't.type',
                't.project_id',
                't.plan_start_at',
                't.start_at',
                't.plan_complete_at',
                't.complete_at',
                't.plan_product_quantity',
                't.plan_work_hour',
                't.work_hour'
            ])
            ->orderBy('t.start_at')
            ->join('project as p', 'p.id', '=', 't.project_id')
            ->where('p.mark', '=', 1)
            ->whereIn('p.status', [ProjectRepo::STATUS_UN_START, ProjectRepo::STATUS_STARTED]);

        $results = $builder->get()->groupBy('project_id')->toArray();
        self::$specialData = $results;
        return self::$specialData;
    }

    private static function singleRequestData($id)
    {
        $task = Task::find($id);
        if (empty($task)) {
            throw new \Exception('任务不存在');
        }

        // $taskNumber = $task->task_number;
        $data = request()->getContent();
        $arr = json_decode($data, true);

        $name = '';

        $projectId = intval($arr['project_id'] ?? '');
        if (empty($projectId)) {
            throw new \Exception('请选择任务所属项目');
        }
        $project = Project::find($projectId);
        if (empty($project)) {
            throw new \Exception('项目信息不存在');
        }
        $description = trim($arr['description'] ?? '');

        $planStartAt = trim($arr['plan_start_at'] ?? '');
        try {
            $planStartAt = Carbon::parse($planStartAt)->toDateString();
        } catch (\Exception $e) {
            throw new \Exception("计划开始时间格式错误");
        }
        $planCompleteAt = trim($arr['plan_complete_at'] ?? '');
        try {
            $planCompleteAt = Carbon::parse($planCompleteAt)->toDateString();
        } catch (\Exception $e) {
            throw new \Exception("计划完成时间格式错误");
        }
        if ($planCompleteAt < $planStartAt) {
            throw new \Exception("计划完成时间不能小于计划开始时间");
        }

        $planProductQuantity = intval($arr['plan_product_quantity'] ?? 0);
        if ($planProductQuantity <= 0) {
            throw new \Exception('计划设备数量错误');
        }

        // 项目任务已用百分比
        $taskPercents = array_sum($project->tasks->pluck('percent')->toArray());
        // 项目百分比
        $taskPercent = $task->percent ?? 0;
        // 剩余百分比
        $percentResidue = 100 - $taskPercents + $taskPercent;

        $percent = 0;

        $status = trim($arr['status'] ?? '');
        $statuses = collect(self::status())->pluck('code')->toArray();
        if (!in_array($status, $statuses)) {
            throw new \Exception("任务状态错误");
        }
        $type = trim($arr['type'] ?? '');
        $types = self::getTypes();
        if (!in_array($type, $types)) {
            throw new \Exception("任务名称错误");
        }

        $userIds = $arr['user_ids'] ?? [];
        $currentUserIds = [];
        foreach ($userIds as $userId) {
            $user = UserModel::find($userId);
            if (empty($user)) {
                throw new \Exception("负责人不存在(#{$userId})");
            }
            $currentUserIds[] = $userId;
        }
        return [
            'main' => [
                'name' => $name,
                'project_id' => $projectId,
                'type' => $type,
                'plan_start_at' => $planStartAt,
                'plan_complete_at' => $planCompleteAt,
                'plan_product_quantity' => $planProductQuantity,
                'percent' => $percent,
                'description' => $description,
                'status' => $status,
                'create_user_id' => registry('user_id')
            ],
            'user_ids' => $userIds
        ];
    }

    private static function requestData()
    {
        $data = request()->getContent();
        $arr = json_decode($data, true);

        $name = '';

        $projectId = intval($arr['project_id'] ?? '');
        if (empty($projectId)) {
            throw new \Exception('请选择任务所属项目');
        }

        $project = ProjectRepo::fetch($projectId);
        $projectUserId = $project['user_id'] ?? 0;
        $currentUserId = registry('user_id') ?? 0;
        if ($currentUserId != 1 && $currentUserId != $projectUserId) {
            throw new \Exception('你没有权限创建任务(仅超级管理员和项目负责人)');
        }
        $project = Project::find($projectId);
        if (empty($project)) {
            throw new \Exception('项目信息不存在');
        }
        $projectPlanCompleteAt = $project->plan_complete_at;
        $description = trim($arr['description'] ?? '');
        $items = $arr['items'] ?? [];

        $types = self::getTypes();
        $tasks = [];

        foreach ($items as $index => $item) {
            $tempIndex = $index + 1;
            $type = trim($item['type'] ?? '');
            if (!in_array($type, $types)) {
                throw new \Exception("任务名称错误: {$type}");
            }
            $planStartAt = trim($item['plan_start_at'] ?? '');
            try {
                $planStartAt = Carbon::parse($planStartAt)->toDateString();
            } catch (\Exception $e) {
                throw new \Exception("任务明细 第{$tempIndex}项 计划开始时间格式错误");
            }
            $planCompleteAt = trim($item['plan_complete_at'] ?? '');
            $planProductQuantity = intval($item['plan_product_quantity'] ?? 0);
            if ($planProductQuantity <= 0) {
                throw new \Exception('计划设备数量错误');
            }
            try {
                $planCompleteAt = Carbon::parse($planCompleteAt)->toDateString();
            } catch (\Exception $e) {
                throw new \Exception("任务明细 第{$tempIndex}项 计划完成时间格式错误");
            }
            if ($planCompleteAt > $projectPlanCompleteAt) {
                throw new \Exception('任务计划完成时间不能超出项目计划完成时间');
            }

            if ($planCompleteAt < $planStartAt) {
                throw new \Exception("任务明细 第{$tempIndex}项 计划完成时间不能小于计划开始时间");
            }
            $percent = 0;
            $planWorkHour = floatval($item['plan_work_hour'] ?? '');
            $userIds = $item['user_ids'] ?? [];
            if (empty($userIds)) {
                throw new \Exception("任务明细 第{$tempIndex}项 负责人信息为空");
            }

            $currentUserIds = [];
            foreach ($userIds as $userId) {
                $user = UserModel::find($userId);
                if (empty($user)) {
                    throw new \Exception("任务明细 第{$tempIndex}项 负责人不存在(#{$userId})");
                }
                $currentUserIds[] = $userId;
            }
            $tasks[] = [
                'main' => [
                    'name' => $name,
                    'project_id' => $projectId,
                    'plan_product_quantity' => $planProductQuantity,
                    'type' => $type,
                    'plan_start_at' => $planStartAt,
                    'plan_complete_at' => $planCompleteAt,
                    'percent' => $percent,
                    'plan_work_hour' => $planWorkHour,
                    'description' => $description,
                    'status' => self::STATUS_UN_START,
                    'create_user_id' => registry('user_id')
                ],
                'user_ids' => $userIds
            ];
        }
        if (empty($tasks)) {
            throw new \Exception("无有效任务数据");
        }
        return $tasks;
    }

    private static function generateNumber($id)
    {
        $task = Task::find($id);
        if (!empty($task)) {
            return $task->task_number;
        }
        $number = date('ymd') . rand(100000, 999999);
        $task = Task::byNumber($number)->first();
        if (!empty($task)) {
            return self::generateNumber($id);
        }
        return $number;
    }
}